#include "RedisConnetionPool.h"

#include <string.h>

RedisConntion::RedisConntion(const char* ip,int port,const char* auth)
    :m_ip(ip),m_port(port),m_auth(auth) {

    init();
}

RedisConntion::~RedisConntion() {
    if(context != NULL) {
        redisFree(context);
    }
}

int RedisConntion::init() {
    redisReply* reply;

    context = redisConnect(m_ip.c_str(),m_port);
    if(context == NULL) {
        return -1;
    }
    if(m_auth.length() > 0) {
        reply = static_cast<redisReply*>(redisCommand(context,"auth %s",m_auth.c_str()));
        if(!checkReply(reply)) {
            if(reply) {
                setErrorInfo(reply->str,reply->len);
            }

            return -1;
        }

        freeReply(reply);
    }

    return 0;
}

void RedisConntion::setErrorInfo(const char* err,int len) {
    if(err && len > 0) {
        errorstr.clear();
        errorstr.assign(err,err + len);
    }
}

const char* RedisConntion::getLastError() {
    return errorstr.c_str();
}

bool RedisConntion::isEffective() {
    return (context && context->err == 0);
}

bool RedisConntion::checkReply(const redisReply *reply) {
    if(NULL==reply) {
        return false;
    }

    switch(reply->type){
    case REDIS_REPLY_STRING:{
            return true;
        }
    case REDIS_REPLY_ARRAY:{
        return true;
        }
    case REDIS_REPLY_INTEGER:{
            return true;
        }
    case REDIS_REPLY_NIL:{
            return false;
        }
    case REDIS_REPLY_STATUS:{
            return (strcasecmp(reply->str,"OK") == 0 ||
                    strcasecmp(reply->str,"QUEUED"))?true:false;
        }
    case REDIS_REPLY_ERROR:{
            return false;
        }
    default:{
            return false;
        }
    }

    return false;
}

void RedisConntion::freeReply(const redisReply *reply){
    if (NULL!=reply) {
        freeReplyObject((void*)reply);
    }
}

bool RedisConntion::get(const char* key,std::string& value) {
    redisReply* reply;

    reply = static_cast<redisReply*>(redisCommand(context,"get %s",key));
    if(checkReply(reply)) {
        value.assign(reply->str,reply->len);
        freeReply(reply);

        return true;
    }

    freeReply(reply);

    return false;
}

bool RedisConntion::set(const char* key,const char* value) {
    redisReply* reply;

    reply = static_cast<redisReply*>(redisCommand(context,"set %s %s",key,value));
    if(checkReply(reply)) {
        freeReply(reply);

        return true;
    }

    freeReply(reply);

    return false;
}

bool RedisConntion::set(const char* key,const char* value,int ex) {
    redisReply* reply;

    reply = static_cast<redisReply*>(redisCommand(context,"set %s %s ex %d",key,value,ex));
    if(checkReply(reply)) {
        freeReply(reply);

        return true;
    }

    freeReply(reply);

    return false;
}

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////

RedisConnectionPool::RedisConnectionPool(const char* ip,int port,const char* auth = NULL,
                                         int init_count,int max_count):
    m_ip(ip),m_port(port),m_auth(auth),m_init_count(init_count),m_max_count(max_count)
{

}

int RedisConnectionPool::init() {
    int i;
    CacheConnection* conn;

    for(i=0;i<m_init_count;i++) {
        conn = new RedisConntion(m_ip.c_str(),m_port,m_auth.c_str());
        if (conn && conn->isEffective()) {
            if(conn->init() != 0) {
                return -1;
            }
            m_free_conn_list.push_back(conn);

        }else {
            delete conn;

            return -1;
        }
    }

    m_count = m_init_count;

    return 0;
}

RedisConnectionPool::~RedisConnectionPool() {
    int i;
    CacheConnection* conn;

    for(i=0;i<m_count;i++) {
        conn = m_free_conn_list.front();
        m_free_conn_list.pop_front();

        delete conn;
    }
}

CacheConnection* RedisConnectionPool::getCacheConnection() {
    CacheConnection* conn;
    std::unique_lock<std::mutex> _lock(m_mutex);
    int conn_count = m_free_conn_list.size();

    if(conn_count > 0) {
        conn = m_free_conn_list.front();
        m_free_conn_list.pop_front();

        return conn;
    }else if(m_count < m_max_count) {
        conn = new RedisConntion(m_ip.c_str(),m_port,m_auth.c_str());
        if(conn && conn->isEffective()) {
            m_count++;

            return conn;
        }else{
            return NULL;
        }
    }
}

void RedisConnectionPool::putCacheConnection(CacheConnection* conn) {
    if(conn && conn->isEffective()) {
        std::unique_lock<std::mutex> _lock(m_mutex);
        m_free_conn_list.push_back(conn);
    }else {
        delete conn;
    }
}

